#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include <unistd.h>
#include <device.h>
#include <string.h>
#include <peekpoke.h>
#include <conio.h>

#include "Global.h"
#include "BbsControlData.h"
#include "Users.h"
#include "Session.h"
#include "Input.h"
#include "Output.h"
#include "StringTable.h"
#include "OverlayManagement.h"
#include "Subs.h"
#include "UserPortRS232.h"
#include "Empire.h"

#pragma code-name("MAINCODE");
#pragma rodata-name("MAINCODE");
#pragma data-name("MAINDATA");
#pragma bss-name("MAINBSS");

void displayLastCallers(void);
void displayPrompt(void);
void displayUserInfo(void);
void displayNews(void);
void mainMenu(void);
void goodbye(bool quick);
void postCall(void);
void modules(void);
void pageSysop(void);

void loggedIn(void)
{
	displayNews();

	pause(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM));

	if(_connectState == CS_DISCONNECTED) return;

	displayUserInfo();

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
	pause(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM));

	if(_connectState == CS_DISCONNECTED) return;

	mainMenu();

	//goodbye(false);
	postCall();
}

void displayUserInfo(void)
{
	static char buffer[10];
	static callHistory_t callHistory;
	static FILE *file;
	static char oldDirectory[17];
	static char directory[17];
	static char filename[21];
	static int i;

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_USER_HEADER_1);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_USER_HEADER_2);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), _currentUser.decorated_username);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_USER_HEADER_3);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), _currentUser.firstName);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_USER_HEADER_4);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), _currentUser.lastName);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_USER_HEADER_5);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), _currentUser.phone);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_USER_HEADER_6);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), itoa(_currentUser.securityLevel, buffer, 10));
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_USER_HEADER_7);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), itoa(_currentSession.requestedWidth, buffer, 10));
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_USER_HEADER_8);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), itoa(_currentSession.requestedHeight, buffer, 10));

	getcwd(oldDirectory, 17);
	getdevicedir(bcd.dataDirectory.device, directory, 17);
	chdir(directory);

	file = fopen("call-history", "rb");

	memset(&callHistory, 0, sizeof(callHistory_t));

	if(file != NULL)
	{
		if(fread(&callHistory, sizeof(callHistory_t), 1, file) == 1)
		{
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_USER_HEADER_9);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), itoa(callHistory.numberOfCallers + 1, buffer, 10));

			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_USER_HEADER_10);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), callHistory.lastUsers[0].decorated_username);
		}
	}

	fclose(file);

	chdir(oldDirectory);

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);

	pause(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM));

	if(_connectState == CS_DISCONNECTED) return;

	displayLastCallers();
}

void displayNews(void)
{
	outputFile(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), "main.news", &(bcd.dataDirectory));
}

void displayLastCallers(void)
{
	static FILE *file;
	static char oldDirectory[17];
	static char directory[17];
	static char filename[21];
	static char i;
	static callHistory_t callHistory;

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_LAST_CALLERS_1);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_LAST_CALLERS_2);

	getcwd(oldDirectory, 17);
	getdevicedir(bcd.dataDirectory.device, directory, 17);
	chdir(directory);

	file = fopen("call-history", "rb");

	memset(&callHistory, 0, sizeof(callHistory_t));

	if(file != NULL)
	{
		fread(&callHistory, sizeof(callHistory_t), 1, file);
	}

	fclose(file);

	chdir(oldDirectory);

	for(i = 0; i < 10; ++i)
	{
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF LEAD_BLOCK CHX_ORANGE);
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), callHistory.lastUsers[i].decorated_username);
	}

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_LAST_CALLERS_3);
}

void mainMenu(void)
{
	char c = 0;
	bool brk = false;
	bool dcdStart = readDCD();
	connect_states_t originalState = _connectState;

	while(!brk)
	{
		if(dcdStart != readDCD() || 
			originalState != _connectState)
		{
			_connectState = readDCD() ? CS_CONNECTED : CS_DISCONNECTED;
			break;
		}

		if(c == 0) displayPrompt();
		c = tolower(inputChar(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ALPHA_NUMERIC));

		switch(c)
		{
		case '?':
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			outputFile(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), "main.menu", &(bcd.dataDirectory));
			c = 0;
			break;

		case 'p':
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			pageSysop();
			c = 0;
			break;

		case 's':
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			loadOverlay("bbs.subs", &enterSubs, "bbs.main");
			//outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF "Sub-Boards" CRLF);
			c = 0;
			break;

		case 'n':
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			loadOverlay("bbs.new", &newMessageScan, "bbs.main");
			//outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF "Sub-Boards" CRLF);
			c = 0;
			break;

		case 'f':
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF "Files" CRLF);
			c = 0;
			break;

		case 'o':
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF "Options" CRLF);
			c = 0;
			break;

		case 'm':
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			//outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF "Modules" CRLF);
			modules();
			c = 0;
			break;

		case 'z':
			if(_currentUser.securityLevel >= 90)
			{
				outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
				outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF "SYSOP" CRLF);
				c = 0;
			}
			break;

		case 'g':
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			brk = true;
			goodbye(false);
			break;

		case 'q':
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			brk = true;
			goodbye(true);
			break;

		case 'r':
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			displayNews();
			c = 0;
			break;
		}
	}
}

void displayPrompt(void)
{
	static char buffer[12];
	time_t time = _systime() + _baseTime;

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_MENU_PROMPT_1);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), _currentUser.decorated_username);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_MENU_PROMPT_2);
	sprintf(buffer, "%u", _currentUser.securityLevel);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_MENU_PROMPT_3);
	strftime(buffer, 12, "%I:%M:%S %p", localtime(&time));
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_MENU_PROMPT_4);		
}

void goodbye(bool quick)
{
	if(!quick)
	{
		outputFile(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), "main.bye", &(bcd.dataDirectory));

		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), CRLF);
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_GOODBYE_1);
		//pause(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM));
	}

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_GOODBYE_CLICK);

	if(readDCD())
	{
		hangUpCall();
	}

	outputCharacter(PLATFORM_ENCODING, OD_MODEM, '\n');
	outputCharacter(PLATFORM_ENCODING, OD_MODEM, '\n');

}


void postCall(void)
{
	static FILE *file;
	static char oldDirectory[17];
	static char directory[17];
	static char filename[21];
	static char i;
	static bool deleteFile;
	static callHistory_t callHistory;

	getcwd(oldDirectory, 17);
	getdevicedir(bcd.dataDirectory.device, directory, 17);
	chdir(directory);

	file = fopen("call-history", "rb");

	memset(&callHistory, 0, sizeof(callHistory_t));

	deleteFile = false;

	if(file != NULL)
	{
		fread(&callHistory, sizeof(callHistory_t), 1, file);

		deleteFile = true;
	}

	fclose(file);

	if(deleteFile) 
	{
		remove("call-history.bak");
		rename("call-history", "call-history.bak");
	}


	for(i = 9; i > 0; --i)
	{
		memcpy(&(callHistory.lastUsers[i]), &(callHistory.lastUsers[i - 1]), sizeof(user_t));

		sprintf(filename, "%03u" CHX_WHITE ": " CHX_LBLUE, i);
		outputString(PLATFORM_ENCODING, OD_SCREEN, CRLF LEAD_BLOCK CHX_ORANGE);
		outputString(PLATFORM_ENCODING, OD_SCREEN, filename);
		outputString(PLATFORM_ENCODING, OD_SCREEN, callHistory.lastUsers[i].decorated_username);
	}

	memcpy(&(callHistory.lastUsers[0]), &_currentUser, sizeof(user_t));

	memcpy(&(callHistory.lastSession), &_currentSession, sizeof(session_t));

	callHistory.numberOfCallers++;

	callHistory.lastLogoffDateTime = _systime() + _baseTime;


	file = fopen("call-history", "wb");

	if(file != NULL)
	{
		fwrite(&callHistory, sizeof(callHistory_t), 1, file);
	}

	fclose(file);

	chdir(oldDirectory);
}

void modulesList(void)
{
	// SYSOPS!
	// You should write strings to the string table and populate this method
	// to match the menu you have implemented in the modules method.
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MODS_MENU_1);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MODS_MENU_2);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MODS_MENU_3);

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MODS_ITEMS_1);
}

void modulesPrompt(void)
{
	static char buffer[12];
	time_t time = _systime() + _baseTime;

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_MODS_PROMPT_1);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), _currentUser.decorated_username);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_MODS_PROMPT_2);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_MODS_PROMPT_3);
	strftime(buffer, 12, "%I:%M:%S %p", localtime(&time));
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), buffer);
	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_MODS_PROMPT_4);		
}

void modules(void)
{
	char c = 0;
	bool brk = false;
	bool dcdStart = readDCD();
	connect_states_t originalState = _connectState;

	while(!brk)
	{
		if(dcdStart != readDCD() || 
			originalState != _connectState)
		{
			_connectState = readDCD() ? CS_CONNECTED : CS_DISCONNECTED;
			break;
		}

		if(c == 0) modulesPrompt();
		c = tolower(inputChar(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ALPHA_NUMERIC));

		switch(c)
		{
		case '?':
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			modulesList();
			c = 0;
			break;

		case 'e': 
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MODS_LOADING_EMPIRE);
			loadOverlay("bbs.empire", &enterEmpire, "bbs.main");
			c = 0;
			break;

		case 'q': case 'Q':
			outputCharacter(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), c);
			brk = true;
			break;
		}
	}
}

bool siren(int loops)
{
	static char n, c, i;
	c = 0;
	POKE(54296, 15);
	POKE(54278, 240);
	POKE(54276, 33);
	while(!kbhit() && c < loops)
	{
		++c;
		for(n = 0; n < 100; ++n)
		{
			POKE(54273, 15 + abs(50 - n));
			for(i = 0; i < 50; ++i);
		}
	}
	POKE(54276, 0);
	POKE(54296, 0);

	return kbhit();
}

void pageSysop(void)
{
	static char counter;
	static char buffer[81];
	static int i;

	counter = 0;

	if(_currentSession.pagedSysop)
	{
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_ALREADY_PAGED);
		return;
	}

	outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_PAGE_SYSOP);
	input(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), IO_ANY_CHARACTER, buffer, 30, false);

	if(strlen(buffer) > 0)
	{
		outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_PAGING_SYSOP);
		while(!siren(2) && counter++ < 10);

		if(counter >= 10)
		{
			outputString(PLATFORM_ENCODING, (outDirections_t)(OD_SCREEN | OD_MODEM), MAIN_SYSOP_UNAVAILABLE);
			_currentSession.pagedSysop = true;
		}
	}

}